package server.util;

import adventure.vocation.Vocation;
import adventure.vocation.VocationEdit;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.SocketException;
import player.Player;
import player.PlayerImp;
import player.PlayerManager;

/**
 *
 * @author Michael Hanns
 *
 */
public class LoginManagerImp implements LoginManager {

    private PlayerManager players;
    private Player activePlayer;
    private boolean loggedOn;
    private BufferedReader in;
    private PrintWriter out;
    private int serverCapacity;

    public LoginManagerImp(BufferedReader read, PrintWriter write, PlayerManager p, int capacity) {
        this.in = read;
        this.out = write;
        this.players = p;
        this.serverCapacity = capacity;
        this.loggedOn = false;
    }

    // Returns true if connection is to be maintained.
    @Override
    public boolean loginPrompt() {
        try {
            // Take the name
            String name = FixTelnetInput.fix(in.readLine());

            if (!shouldInputProgress(name)) {
                return false;
            }

            // If the name is okay and the player isn't already logged in...
            if (nameInvalid(name)) {
                return true;
            } else if (playerAlreadyOnline(name)) {
                return true;
            }

            // If the name matches an existing player try and log in...
            if (playerExists(name)) {
                out.println("Player " + players.getOfflinePlayer(name).getAliveName() + " found! Please enter password.\r");
                String password = FixTelnetInput.fix(in.readLine());

                if (!shouldInputProgress(password)) {
                    return false;
                }

                if (isLoginSuccessful(name, password)) {
                    loggedOn = true;
                    return true;
                } else {
                    return false;
                }
            } // Otherwise, create a character...
            else {
                // Capitalise the first letter of the name if not already
                char[] nameLetters = name.toCharArray();
                nameLetters[0] = Character.toUpperCase(nameLetters[0]);

                name = String.copyValueOf(nameLetters);

                out.println(name + " is not known here. Create new character? [Y/N]\r");
                String createNew = FixTelnetInput.fix(in.readLine());

                if (!shouldInputProgress(createNew)) {
                    return false;
                }

                // Keep taking input until they enter Y or N or quit.
                while (!(createNew.equalsIgnoreCase("Y") || createNew.equalsIgnoreCase("N"))) {
                    out.println("Please enter Y or N (or 'QUIT' to disconnect).\r");
                    createNew = FixTelnetInput.fix(in.readLine());

                    if (!shouldInputProgress(createNew)) {
                        return false;
                    }
                }

                if (!shouldCreateCharacter(createNew)) {
                    return true;
                }

                // We have confirmed they DO want to create a character, so let's create!
                String password = takeNewPassword(name);

                if (!shouldInputProgress(password)) {
                    return false;
                }


                // Choose Vocation. If they input shouldn't progress, returns null
                Vocation vocation = selectVocation();

                if (vocation == null) {
                    return false;
                }

                // Everything is taken! Create the character! If it fails, disconnect
                return createCharacter(name, password, vocation);
            }
        } catch (SocketException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    private boolean shouldInputProgress(String input) {
        if (input == null) {
            out.println("Input null, closing connection...\r");
            return false;
        } else if (playerDisconnects(input)) {
            return false;
        } else if (maximumPlayersReached()) {
            return false;
        }
        return true;
    }

    private boolean maximumPlayersReached() {
        if (players.onlinePlayers() >= serverCapacity) {
            out.println("The server has reached maximum capacity. Please try again later.\r");
            return true;
        } else {
            return false;
        }
    }

    private boolean nameInvalid(String name) {
        if (name.length() < 3 || name.length() > 12) {
            out.println("Invalid name! Your name must be between 3 and 12 characters long.\r");
            out.println("Please re-enter your name.\r");
            return true;
        } else if (name.contains(" ")) {
            out.println("Invalid name! You may not include spaces in your name.\r");
            out.println("Please re-enter your name.\r");
            return true;
        }
        return false;
    }

    private boolean playerDisconnects(String input) {
        if (input.equalsIgnoreCase("DISCONNECT") || input.equalsIgnoreCase("QUIT")) {
            return true;
        }
        return false;
    }

    private boolean playerExists(String player) {
        if (players.getOfflinePlayer(player).getID() > 0) {
            return true;
        }
        return false;
    }

    private boolean playerAlreadyOnline(String player) {
        if (players.getOnlinePlayer(player).getID() > 0) {
            out.println("A player by that name is already playing!\r\n");
            out.println("Please re-enter your name.");
            return true;
        }
        return false;
    }

    private boolean isLoginSuccessful(String name, String password) {
        activePlayer = players.getOfflinePlayer(name);

        if (players.logOn(activePlayer.getAliveName(), password)) {
            out.println("Success! Entering world...\r");
            return true;
        } else {
            out.println("Incorrect password!\r\n");
            return false;
        }
    }

    private boolean shouldCreateCharacter(String createNew) throws IOException {
        if (createNew.equalsIgnoreCase("Y")) {
            return true;
        } else {
            out.println("Please re-enter your name.\r");
            return false;
        }
    }

    private String takeNewPassword(String name) throws IOException {
        out.println("Please enter a password for " + name + " (or 'QUIT' to disconnect):\r");
        String password = FixTelnetInput.fix(in.readLine());

        // If invalid, send it back - it'll be checked and thrown away in loginPrompt()
        if (!shouldInputProgress(password)) {
            return password;
        }

        // If the password is valid, carry on.. otherwise take again.
        if (passwordInvalid(password)) {
            return takeNewPassword(name);
        }

        out.println("Please confirm password for " + name + " (or 'QUIT' to disconnect):\r");
        String passwordConfirm = FixTelnetInput.fix(in.readLine());

        // If invalid, send it back - it'll be checked and thrown away in loginPrompt()
        if (!shouldInputProgress(passwordConfirm)) {
            return password;
        }

        // If the password is valid, carry on.. otherwise take again.
        if (passwordInvalid(passwordConfirm)) {
            return takeNewPassword(name);
        }

        // If passwords don't match, take again..
        if (!passwordsMatch(password, passwordConfirm)) {
            return takeNewPassword(name);
        }
        return password;
    }

    private boolean passwordInvalid(String password) {
        if (password.length() < 3 || password.length() > 12) {
            out.println("Invalid password! Passwords must be between 3 and 12 characters long.\r");
            return true;

        } else if (password.contains(" ")) {
            out.println("Invalid password! You may not include spaces in your password.\r");
            return true;
        }
        return false;
    }

    private boolean passwordsMatch(String password, String passwordConfirm) {
        if (password.equals(passwordConfirm)) {
            return true;
        } else {
            out.println("Passwords do not match!\r");
            return false;
        }
    }

    // Presents vocation options and returns a Vocation based on the players
    // input. If they type quit or something, returns null;
    private Vocation selectVocation() throws IOException {
        out.println("\r\nWhich vocation would you like to choose?\r");
        out.println("Type the vocation's name to read more.\r");
        String[] vocations = players.getVocationNames();
        String vocationsOut = "";
        for (int x = 0; x < vocations.length; x++) {
            vocationsOut += vocations[x] + " ";
        }
        out.println(vocationsOut);

        String vocSelect = FixTelnetInput.fix(in.readLine());
        if (!shouldInputProgress(vocSelect)) {
            return null;
        }

        for (int x = 0; x < vocations.length; x++) {
            if (vocations[x].equalsIgnoreCase(vocSelect)) {
                displayVocation(x + 1);


                out.println("\r\nDo you wish to play this vocation? [Y/N]\r");
                String confirmVocation = FixTelnetInput.fix(in.readLine());

                if (!shouldInputProgress(confirmVocation)) {
                    return null;
                }

                // Keep taking input until they enter Y or N or quit.
                while (!(confirmVocation.equalsIgnoreCase("Y") || confirmVocation.equalsIgnoreCase("N"))) {
                    out.println("Please enter Y or N (or 'QUIT' to disconnect).\r");
                    confirmVocation = FixTelnetInput.fix(in.readLine());

                    if (!shouldInputProgress(confirmVocation)) {
                        return null;
                    }
                }
                
                if (confirmVocation.equalsIgnoreCase("Y")) {
                    return ((VocationEdit) players.getVocation(x + 1)).cloneThis();
                } else {
                    return selectVocation();
                }
            }
        }

        out.println("That is not a valid vocation!\r");
        return selectVocation();
    }

    private void displayVocation(int id) throws IOException {
        Vocation voc = players.getVocation(id);
        out.println("\n\r" + voc.getName() + "\n\r-----");
        out.println(voc.getDescription() + "\r\n");

        /*
         * Table:
         * +------------------+-----+-----+-----+-----+-----+
         * |                  | ATK | DEF | INT | SPD | LCK |
         * +------------------+-----+-----+-----+-----+-----+
         * | Base:            | 23  | 343 | 343 | 343 | 343 |
         * | Increase Chance: | 10% | 20% | 34% |     |     |
         * + -----------------+-----+-----+-----+-----+-----+
         */

        out.println(" +------------------+-----+-----+-----+-----+-----+\r");
        out.println(" |                  | ATK | DEF | INT | SPD | LCK |\r");
        out.println(" +------------------+-----+-----+-----+-----+-----+\r");
        out.println(" | Base:            | " + getWhiteSpace(voc.getBaseAttack())
                + "  | " + getWhiteSpace(voc.getBaseDefence())
                + "  | " + getWhiteSpace(voc.getBaseIntel()) + "  | "
                + getWhiteSpace(voc.getBaseSpeed())
                + "  | " + getWhiteSpace(voc.getBaseLuck()) + "  |\r");
        out.println(" | Increase Chance: | " + getWhiteSpace((int) (voc.getChanceAttack() * 100))
                + "% | " + getWhiteSpace((int) (voc.getChanceDefence() * 100))
                + "% | " + getWhiteSpace((int) (voc.getChanceIntel() * 100))
                + "% | " + getWhiteSpace((int) (voc.getChanceSpeed() * 100))
                + "% | " + getWhiteSpace((int) (voc.getChanceLuck() * 100)) + "% |\r");
        out.println(" +------------------+-----+-----+-----+-----+-----+\r");
    }

    private boolean createCharacter(String name, String password, Vocation vocation) {
        if (players.createPlayer(name, password, vocation)) {
            out.println("Player " + name + " created!\r\n");
            out.println("Please re-enter your name.\r");
            return true;
        } else {
            out.println("Error creating player: unable to save file. Please contact your server administrator.\r");
            return false;
        }
    }

    private String getWhiteSpace(int stat) {
        if (stat < 10) {
            return stat + " ";

        } else {
            return stat + "";
        }
    }

    @Override
    public boolean loggedOn() {
        return loggedOn;
    }

    @Override
    public Player getCharacter() {
        if (loggedOn) {
            return activePlayer;
        } else {
            return new PlayerImp();
        }
    }
}
